#ifndef NANE_MODULEMANAGER_H
#   define NANE_MODULEMANAGER_H

#   include "nane/core/types.h"
#   include "nane/core/Holder.h"
#   include "nane/core/IntrusivePtr.h"
#   include "nane/core/crt/IModuleSystem.h"
#   include "nane/core/crt/Context.h"

namespace nane
{
    template<class S>
    class ModuleSystemHolder
        : public Holder<S>
    {
    };
    
    template<>
    class ModuleSystemHolder<IModuleSystem>
        : public Holder<IModuleSystem>
    {
    public:
        static IModuleSystem* Instance()
        {
            if( s_instance == NULL )
            {
                s_instance = nane::m_context->moduleSystem;
            }
            return s_instance;            
        }
    };
    
    class Module;
    typedef IntrusivePtr<IModule> ModulePtr;
    
    class ModuleManager
    {
    public:
        static bool Initialize();
        static void Shutdown();
        
        static bool RegisterStaticModule( const ModuleInfo* _moduleInfo )
        {
            return ModuleSystemHolder<IModuleSystem>::Instance()->RegisterStaticModule(_moduleInfo);
        }
        
        static ModulePtr LoadModule( const char* _moduleName )
        {
            IModule* moduleInterface = ModuleSystemHolder<IModuleSystem>::Instance()->LoadModule(_moduleName);
            return ModulePtr(moduleInterface);
        }
        
        static ModulePtr GetModuleByInterface( const char* _interfaceName )
        {
            IModule* moduleInterface = ModuleSystemHolder<IModuleSystem>::Instance()->GetModuleByInterface(_interfaceName);
            return ModulePtr(moduleInterface);
        }
        
        template<typename F>
        static void ForeachModule( const char* _interfaceName, F _functor )
        {
            ModuleApply<F> moduleApply(_functor);
            ForeachModule<IModuleSystem::ModuleApplyBase*>(_interfaceName, &moduleApply);
        }
        
    public:
        template<typename F>
        struct ModuleApply
            : public IModuleSystem::ModuleApplyBase
        {
            F m_f;
            ModuleApply( F _f )
                : m_f( _f )
            {
            }
            
            void Apply( IModule* _module )
            {
                m_f(ModulePtr(_module));
            }
        };
        
    public:
        class Implementation;   // we know nothing about it from here even if it public
    };
    
    template<>
    inline void ModuleManager::ForeachModule<IModuleSystem::ModuleApplyBase*>( const char* _interfaceName, IModuleSystem::ModuleApplyBase* _functor )
    {
        ModuleSystemHolder<IModuleSystem>::Instance()->ForeachModule(_interfaceName, _functor);
    }
    
}   // namespace nane

#endif	// NANE_MODULEMANAGER_H
